package io.xdag.crypt;

import java.math.BigInteger;
import java.util.List;

import io.xdag.crypt.enumeration.EDfsEnmix;
import io.xdag.crypt.enumeration.EDfslibErrors;
import io.xdag.crypt.model.DfsABCDXYZT;
import io.xdag.crypt.model.DfslibCrypt;
import io.xdag.crypt.model.DfslibCryptRegs;
import io.xdag.crypt.model.DnetKey;
import io.xdag.crypt.model.Sector;
import io.xdag.crypt.model.Uint16;

public class DnetCrypt {

	private static int G_KEYLEN = 0;

	private static final int DNET_KEYLEN = 256;

	private static DnetKey gDnetKeys;

	// private static DfslibCrypt dfsc;

	private static DfslibCrypt gDnetUserCrypt;

	/**
	 * 质数
	 */
	private static long DFS_MAGIC0 = 572035291L;
	private static long DFS_MAGIC1 = 2626708081L;
	private static long DFS_MAGIC2 = 2471573851L;
	private static long DFS_MAGIC3 = 3569250857L;
	private static long DFS_MAGIC4 = 1971772241L;
	private static long DFS_MAGIC5 = 1615037507L;
	private static long DFS_MAGIC6 = 43385317L;
	private static long DFS_MAGIC7 = 1229426917L;
	private static long DFS_MAGIC8 = 3433359571L;

	public DnetCrypt() {

	}

	public static void main(String[] args) {
		long starTime = System.currentTimeMillis();
		setUserCrypt("jerry221");
		long endTime = System.currentTimeMillis();
		long Time = endTime - starTime;
		System.out.println(Time);
	}

	public static Long dfsCrypt0(Long x, Long y, Long z, Long t) {
		// Long a = ((Long) (((Long) y * (z + dfsc.getRegs()[x.intValue() >> 16])) >>
		// 16) ^ dfsc.getRegs()[t.intValue()]);
		return null;
	}

	/**
	 * 测试key(公钥、私钥)
	 * 
	 * @return
	 */
	public static int dnetTestKeys() {
		long[] src = new long[128], dest = new long[128];
		return 0;
	}

	/**
	 * 
	 * @param src
	 *            64位数组
	 * @return
	 */
	public static int dnetRandomSector(long[] src) {
		String random = "Iyf&%d#$jhPo_t|3fgd+hf(s@;)F5D7gli^kjtrd%.kflP(7*5gt;Y1sYRC4VGL&";
		return 0;
	}

	public static int dnetDetectKeylen(List<Long> key, int keylen) {
		return keylen;
	}

	public static int arrayLongCmp(Long[] key, int start, int count) {
		int i = 0;
		int j = start;
		if (count > key.length) {
			count = key.length - start - 1;
		}
		while (i < count && count > 0 && (key[i] != key[j])) {
			i++;
			j++;
			count--;
		}
		if (count == 0) {
			return 0;
		}
		if ((key[i] - key[j]) > 0) {
			return 1;
		} else {
			return -1;
		}
	}
	public static DfsABCDXYZT dfsCryptXYZT(DfsABCDXYZT abcdxyzt, DfslibCrypt dfsc) {
		int num = 0;
//		abcdxyzt.a.set(0);
//		abcdxyzt.b.set(0);
//		abcdxyzt.c.set(0);
//		abcdxyzt.d.set(0);
		for (int i = 0; i < 8; i++) {
			num = (int)(abcdxyzt.t.getValue()&0xFFFF);
			BigInteger cryptNum = new BigInteger(abcdxyzt.y.toString()).multiply(new BigInteger(Long.toString((abcdxyzt.z.get() + dfsc.regs[(int) (abcdxyzt.x.get() >> 16)].get())&0xFFFFFFFFL)))
					.shiftRight(16).and(new BigInteger("FFFFFFFF",16));
			abcdxyzt.a.set(cryptNum.longValue() ^ dfsc.regs[num].get());
			num = (int)(abcdxyzt.x.getValue()&0xFFFF);
			cryptNum = new BigInteger(abcdxyzt.z.toString()).multiply(new BigInteger(Long.toString((abcdxyzt.t.get() + dfsc.regs[(int) (abcdxyzt.y.get() >> 16)].get())&0xFFFFFFFFL)))
					.shiftRight(16).and(new BigInteger("FFFFFFFF",16));
			abcdxyzt.b.set(cryptNum.longValue() ^ dfsc.regs[num].get());
			num = (int)(abcdxyzt.y.getValue()&0xFFFF);
			cryptNum = new BigInteger(abcdxyzt.t.toString()).multiply(new BigInteger(Long.toString((long)(abcdxyzt.x.get() + dfsc.regs[(int) (abcdxyzt.z.get() >> 16)].get())&0xFFFFFFFFL)))
					.shiftRight(16).and(new BigInteger("FFFFFFFF",16));
			abcdxyzt.c.set(cryptNum.longValue()^ dfsc.regs[num].get());
			num = (int)(abcdxyzt.z.getValue()&0xFFFF);
			cryptNum = new BigInteger(abcdxyzt.x.toString()).multiply(new BigInteger(Long.toString((abcdxyzt.y.get() + dfsc.regs[(int) (abcdxyzt.t.get() >> 16)].get())&0xFFFFFFFFL)))
					.shiftRight(16).and(new BigInteger("FFFFFFFF",16));
			abcdxyzt.d.set(cryptNum.longValue()^ dfsc.regs[num].get());
			num = (int)(abcdxyzt.d.getValue()&0xFFFF);
			cryptNum = new BigInteger(abcdxyzt.b.toString()).multiply(new BigInteger(Long.toString((abcdxyzt.c.get() + dfsc.regs[(int) (abcdxyzt.a.get() >> 16)].get())&0xFFFFFFFFL)))
					.shiftRight(16).and(new BigInteger("FFFFFFFF",16));
			abcdxyzt.x.set(cryptNum.longValue()^ dfsc.regs[num].get());
			num = (int)(abcdxyzt.a.getValue()&0xFFFF);
			cryptNum = new BigInteger(abcdxyzt.c.toString()).multiply(new BigInteger(Long.toString((abcdxyzt.d.get() + dfsc.regs[(int) (abcdxyzt.b.get() >> 16)].get())&0xFFFFFFFFL)))
					.shiftRight(16).and(new BigInteger("FFFFFFFF",16));
			abcdxyzt.y.set(cryptNum.longValue()^ dfsc.regs[num].get());
			num = (int)(abcdxyzt.b.getValue()&0xFFFF);
			cryptNum = new BigInteger(abcdxyzt.d.toString()).multiply(new BigInteger(Long.toString((abcdxyzt.a.get() + dfsc.regs[(int) (abcdxyzt.c.get() >> 16)].get())&0xFFFFFFFFL)))
					.shiftRight(16).and(new BigInteger("FFFFFFFF",16));
			abcdxyzt.z.set(cryptNum.longValue()^ dfsc.regs[num].get());
			num = (int)(abcdxyzt.c.getValue()&0xFFFF);
			cryptNum = new BigInteger(abcdxyzt.a.toString()).multiply(new BigInteger(Long.toString((abcdxyzt.b.get() + dfsc.regs[(int) (abcdxyzt.d.get() >> 16)].get())&0xFFFFFFFFL)))
					.shiftRight(16).and(new BigInteger("FFFFFFFF",16));
			abcdxyzt.t.set(cryptNum.longValue()^ dfsc.regs[num].get());
		}
		return abcdxyzt;
	}
	
	/**
	 * 返回需要加密的数据
	 * @param data
	 * @param index
	 * @return
	 */
	private static <T> long getEncryptData(T data,int index){
		long tmp = 0L;
		if(data instanceof DfslibCrypt) {
			tmp = ((DfslibCrypt)data).regs[index].get();
		}
		if(data instanceof Sector) {
			tmp = ((Sector)data).sector0[index].get();
		}
		return tmp;
	}
	/**
	 * 
	 * @param abcdxyzt
	 * @param data
	 * @param index
	 * @param type 1 参数d 2参数t
	 * @return
	 */
	private static <T> T setEncryptData(DfsABCDXYZT abcdxyzt,T data,int index,int type){
		if(data instanceof DfslibCrypt) {
			if(type == 1) {
				((DfslibCrypt)data).regs[index].set( ((DfslibCrypt)data).regs[index].get()-abcdxyzt.d.get());
			}
			if(type == 2) {
				((DfslibCrypt)data).regs[index].set( ((DfslibCrypt)data).regs[index].get()-abcdxyzt.t.get());
			}
		}
		if(data instanceof Sector) {
			if(type == 1) {
				((Sector)data).sector0[index].set(((Sector)data).sector0[index].get()-abcdxyzt.d.get());
			}
			if(type == 2) {
				((Sector)data).sector0[index].set(((Sector)data).sector0[index].get()-abcdxyzt.t.get());
			}
		}
		return data;
	}
	public static <T> T dfsEncrypt6(DfsABCDXYZT abcdxyzt, DfslibCrypt dfsc,T data, int sectorNO) {
		int j = sectorNO << 7;
		int num = 0;
		for (int i = 0; i < 8; i++) {
			num = (int)(abcdxyzt.t.getValue()&0xFFFF);
			BigInteger cryptNum = new BigInteger(abcdxyzt.y.toString()).multiply(new BigInteger(Long.toString((abcdxyzt.z.get() + dfsc.regs[(int) (abcdxyzt.x.get() >> 16)].get())&0xFFFFFFFFL)))
					.shiftRight(16).and(new BigInteger("FFFFFFFF",16));
			
			abcdxyzt.a.set(cryptNum.longValue() ^ dfsc.regs[num].get() ^ ~getEncryptData(data,j));
			num = (int)(abcdxyzt.x.getValue()&0xFFFF);
			cryptNum = new BigInteger(abcdxyzt.z.toString()).multiply(new BigInteger(Long.toString((abcdxyzt.t.get() + dfsc.regs[(int) (abcdxyzt.y.get() >> 16)].get())&0xFFFFFFFFL)))
					.shiftRight(16).and(new BigInteger("FFFFFFFF",16));
			abcdxyzt.b.set(cryptNum.longValue() ^ dfsc.regs[num].get());
			num = (int)(abcdxyzt.y.getValue()&0xFFFF);
			cryptNum = new BigInteger(abcdxyzt.t.toString()).multiply(new BigInteger(Long.toString((long)(abcdxyzt.x.get() + dfsc.regs[(int) (abcdxyzt.z.get() >> 16)].get())&0xFFFFFFFFL)))
					.shiftRight(16).and(new BigInteger("FFFFFFFF",16));
			abcdxyzt.c.set(cryptNum.longValue() ^ dfsc.regs[num].get());
			num = (int)(abcdxyzt.z.getValue()&0xFFFF);
			cryptNum = new BigInteger(abcdxyzt.x.toString()).multiply(new BigInteger(Long.toString((abcdxyzt.y.get() + dfsc.regs[(int) (abcdxyzt.t.get() >> 16)].get())&0xFFFFFFFFL)))
					.shiftRight(16).and(new BigInteger("FFFFFFFF",16));
			abcdxyzt.d.set(cryptNum.longValue() ^ dfsc.regs[num].get());
//			dfsc.regs[j].set(dfsc.regs[j].get() - abcdxyzt.d.get());
			setEncryptData(abcdxyzt,data,j,1);
			j++;
			num = (int)(abcdxyzt.d.getValue()&0xFFFF);
			cryptNum = new BigInteger(abcdxyzt.b.toString()).multiply(new BigInteger(Long.toString((abcdxyzt.c.get() + dfsc.regs[(int) (abcdxyzt.a.get() >> 16)].get())&0xFFFFFFFFL)))
					.shiftRight(16).and(new BigInteger("FFFFFFFF",16));
			abcdxyzt.x.set(cryptNum.longValue() ^ dfsc.regs[num].get() ^ ~getEncryptData(data,j));
			num = (int)(abcdxyzt.a.getValue()&0xFFFF);
			cryptNum = new BigInteger(abcdxyzt.c.toString()).multiply(new BigInteger(Long.toString((abcdxyzt.d.get() + dfsc.regs[(int) (abcdxyzt.b.get() >> 16)].get())&0xFFFFFFFFL)))
					.shiftRight(16).and(new BigInteger("FFFFFFFF",16));
			abcdxyzt.y.set(cryptNum.longValue() ^ dfsc.regs[num].get());
			num = (int)(abcdxyzt.b.getValue()&0xFFFF);
			cryptNum = new BigInteger(abcdxyzt.d.toString()).multiply(new BigInteger(Long.toString((abcdxyzt.a.get() + dfsc.regs[(int) (abcdxyzt.c.get() >> 16)].get())&0xFFFFFFFFL)))
					.shiftRight(16).and(new BigInteger("FFFFFFFF",16));
			abcdxyzt.z.set(cryptNum.longValue() ^ dfsc.regs[num].get());
			num = (int)(abcdxyzt.c.getValue()&0xFFFF);
			cryptNum = new BigInteger(abcdxyzt.a.toString()).multiply(new BigInteger(Long.toString((abcdxyzt.b.get() + dfsc.regs[(int) (abcdxyzt.d.get() >> 16)].get())&0xFFFFFFFFL)))
					.shiftRight(16).and(new BigInteger("FFFFFFFF",16));
			abcdxyzt.t.set(cryptNum.longValue() ^ dfsc.regs[num].get());
//			dfsc.regs[j].set(dfsc.regs[j].get() - abcdxyzt.t.get());
			setEncryptData(abcdxyzt,data,j,2);
			j++;
		}
		return data;
	}

	public static int dfsMod(BigInteger sectorNo, int primeNumber) {
		if (sectorNo.compareTo(new BigInteger("4294967296")) == -1) {
			// 小于unsigned long 范围 2^32-1
			return sectorNo.mod(new BigInteger(Long.toString(primeNumber))).intValue();
		} else {
			BigInteger tmp = sectorNo.shiftRight(32).mod(new BigInteger(Long.toString(primeNumber)));
			tmp = tmp.shiftLeft(16).or(sectorNo.shiftRight(16)).mod(new BigInteger(Long.toString(primeNumber)));
			tmp = tmp.shiftLeft(16).or(sectorNo.and(new BigInteger("65535")));// or sectorNo的低16位
			return tmp.mod(new BigInteger(Long.toString(primeNumber))).intValue();
		}
	}

	/**
	 * 加密前处理
	 * 
	 * @param dfsc
	 * @param sectorNo
	 * @param xyzt
	 * @return
	 */
	public static DfsABCDXYZT dfsPrepare(DfslibCrypt dfsc, BigInteger sectorNo, DfsABCDXYZT xyzt) {
		BigInteger dfsMagic = new BigInteger(Long.toString(DFS_MAGIC7)).shiftLeft(32)
				.or(new BigInteger(Long.toString(DFS_MAGIC8)));
		dfsMagic = dfsMagic.multiply(sectorNo);
		xyzt.x.set(dfsc.pwd[0].get() ^ dfsc.regs[dfsMod(sectorNo, 65479) + 31].get());
		xyzt.y.set(dfsc.pwd[1].get() ^ dfsc.regs[dfsMod(sectorNo, 65497) + 11].get());
		xyzt.z.set(dfsc.pwd[2].get() ^ dfsc.regs[dfsMod(sectorNo, 65519) + 5].get());
		xyzt.t.set(dfsc.pwd[3].get() ^ dfsc.regs[dfsMod(sectorNo, 65521) + 3].get());
		xyzt = dfsCryptXYZT(xyzt, dfsc);
		return xyzt;
	}

	/**
	 * sector函数混淆8次
	 * 
	 * @param <T>
	 * @param sector
	 * @return
	 */
	public static <T> T dfsEnmixSector(T data, int j) {
		long c = DFS_MAGIC5;
		int i = 8;
		if (data instanceof DfslibCrypt) {
			j <<= 7;
		}
		j--;
		j += 128;
		while (i-- > 0) {
			if (data instanceof DfslibCrypt) {
				for (int k = 0; k < EDfsEnmix.DFSENMIX_6.getCode().intValue(); k++) {
					((DfslibCrypt) data).regs[j - k].set(((DfslibCrypt) data).regs[j - k].get() ^ (c * 43385317));
					c = ((DfslibCrypt) data).regs[j - k].get();
				}
			}
			if (data instanceof Sector) {
				for (int k = 0; k < EDfsEnmix.DFSENMIX_6.getCode().intValue(); k++) {
					((Sector) data).sector0[j - k].set(((Sector) data).sector0[j - k].get() ^ (c * 43385317));
					c = ((Sector) data).sector0[j - k].get();
				}
			}
		}
		return data;
	}

	/**
	 * 加密分区数据 执行dfsCrypt6函数8次
	 * 
	 * @param <T>
	 * @param dfsc
	 *            加密对象
	 * @param sector
	 * @param sectorNO
	 * @return
	 */
	/**
	 * 加密分区数据 执行dfsCrypt6函数8次
	 * @param dfsc
	 * @param data 类型为DfslibCrypt则index为索引，类型为Sector则index忽略
	 * @param sectorNO
	 * @param index
	 * @return
	 */
	public static <T> T dfslibEncryptSector(DfslibCrypt dfsc, T data, BigInteger sectorNO, int index) {
		DfsABCDXYZT abcdxyzt = new DfsABCDXYZT();

		if (dfsc.ispwd.get() == 0L) {
			return null;
		}
		abcdxyzt = dfsPrepare(dfsc, sectorNO, abcdxyzt);


		if(data instanceof DfslibCrypt) {
			dfsc = dfsEnmixSector(dfsc, index);
		}
		if(data instanceof Sector) {
			data = dfsEnmixSector(data, index);
		}
		int i = 8;
		while (i-- > 0) {
			data = dfsEncrypt6(abcdxyzt, dfsc, data , index);
		}
		return data;
	}

	public static DfslibCrypt dfslibCryptSetSector0(DfslibCrypt dfsc,Sector sector) {
		byte[] data = new byte[512];
		byte[] bytes = sector.getBytes();
		System.arraycopy(bytes, 0, data, 0, 512);
		byte[] bRegs = new byte[262144];
		if (dfsc.ispwd.get() == 0L) {
			return null;
		}
		for (int i = 0; i < 512; ++i) {
			System.arraycopy(data, i, bRegs, i * 512, 512 - i);
			System.arraycopy(data, 0, bRegs, (i + 1) * 512 - i, i);
		}
		DfslibCryptRegs regs = new DfslibCryptRegs(bRegs);
		dfsc.setRegs(regs.regs);
		for (int i = 0; i < 512; i++) {
			dfsc = dfslibEncryptSector(dfsc, dfsc,new BigInteger(Integer.toString(i)),i);
		}
		return dfsc;
	}

	/**
	 * 设置用户加密
	 * 
	 * @return
	 */
	public static long setUserCrypt(String password) {
		Sector sector0 = new Sector();

		gDnetUserCrypt = dfslibCryptSetPassword(password);
		for (int i = 0; i < 128; ++i) {
			sector0.sector0[i].set(0x4ab29f51L + i * 0xc3807e6dL);
		}
		for (int i = 0; i < 128; i++) {
			gDnetUserCrypt = dfslibCryptSetSector0(gDnetUserCrypt, sector0);
			// ull 64 位整形
			sector0 = dfslibEncryptSector(
					gDnetUserCrypt, sector0, new BigInteger("9d2e61fc538704ab", 16)
							.multiply(new BigInteger(Integer.toString(i))).add(new BigInteger("3e9c1d624a8b570f", 16)),
					0);
		}
//		System.out.println(gDnetUserCrypt);
		return 0;
	}

	/**
	 * 将输入密码加密为4个long型数字
	 * 
	 * @param dfsc
	 * @param password
	 * @return
	 */
	public static DfslibCrypt dfslibCryptSetPassword(String password) {
		DfslibCrypt dfsc = new DfslibCrypt();
		dfsc.ispwd.set(0L);
		dfsc.pwd[0].set(DFS_MAGIC0);
		dfsc.pwd[1].set(DFS_MAGIC1);
		dfsc.pwd[2].set(DFS_MAGIC2);
		dfsc.pwd[3].set(DFS_MAGIC3);
		for (int i = 0; i < password.getBytes().length; i++) {
			long res = dfslibUnicodeRead(password.getBytes()[i]);
			for (int j = 0; j < 4; ++j) {
				res += dfsc.pwd[j].get() * DFS_MAGIC4;
				dfsc.pwd[j].set(res);
				res >>= 32;
			}
		}
		if (password.length() > 0) {
			dfsc.ispwd.set(1);
		}
		return dfsc;
	}

	/**
	 * 读取字符的ascii码
	 * 
	 * @param str
	 * @return
	 */
	public static int dfslibUnicodeRead(byte chr) {
		if (chr < 0) {
			return EDfslibErrors.DFSLIB_NAME_TOO_LONG.getCode();
		}
		if (chr < 128) {
			return chr;
		}
		if (chr < 192) {
			return EDfslibErrors.DFSLIB_INVALID_NAME.getCode();
		}
		if (chr < 224) {
			return EDfslibErrors.DFSLIB_NOT_REALIZED.getCode();
		}
		if (chr < 240) {
			return EDfslibErrors.DFSLIB_NOT_REALIZED.getCode();
		}
		return EDfslibErrors.DFSLIB_INVALID_NAME.getCode();
	}

}
